package com.demo.gulimall.ware.service.impl;

import com.demo.gulimall.common.constant.OrderStatusEnum;
import com.demo.gulimall.common.exception.NoStockException;
import com.demo.gulimall.common.to.mq.StockLockedTo;
import com.demo.gulimall.common.utils.R;
import com.demo.gulimall.ware.entity.*;
import com.demo.gulimall.ware.feign.OrderFeignService;
import com.demo.gulimall.ware.mapper.WareOrderTaskDetailMapper;
import com.demo.gulimall.ware.mapper.WareSkuMapper;
import com.demo.gulimall.ware.service.*;
import com.demo.gulimall.ware.vo.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.stream.Collectors;

/**
 * 商品库存
 *
 * @author Chen Yuan
 */
@Slf4j
@Service
public class WareSkuServiceImpl extends ServiceImpl<WareSkuMapper, WareSku> implements WareSkuService {

    @Autowired
    private WareSkuMapper wareSkuMapper;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Autowired
    private WareOrderTaskService wareOrderTaskService;

    @Autowired
    private WareOrderTaskDetailMapper wareOrderTaskDetailMapper;

    @Autowired
    private WareOrderTaskDetailService wareOrderTaskDetailService;

    @Autowired
    private OrderFeignService orderFeignService;

    @Override
    public List<SkuHasStockVo> getSkuHasStock(List<Long> skuIds) {

        return skuIds.stream().map(sku -> {
            Long stock =  baseMapper.getSkuStock(sku);
            stock = stock==null ? -1L : stock;
            return new SkuHasStockVo(sku, (stock>0));
        }).collect(Collectors.toList());
    }

    // 锁定库存
    @Override
    @Transactional
    public Boolean orderLockStock(SkuLockStockVo lockStockVo) {

        // 增1. 保存库存工作单信息
        WareOrderTask wareOrderTask = new WareOrderTask();
        wareOrderTask.setOrderSn(lockStockVo.getOrderSn());
        wareOrderTaskService.save(wareOrderTask);


        // 1. 查询每个商品在哪个仓库有库存
        List<SkuWareHasStockVo> hasStockVos = lockStockVo.getOrderItems().stream().map(item -> {
            SkuWareHasStockVo stock = new SkuWareHasStockVo();
            Long skuId = item.getSkuId();
            // 查询库存
            List<Long> wareIds = wareSkuMapper.getWareIdHasSkuStock(skuId);
            stock.setSkuId(skuId)
                    .setWareIds(wareIds)
                    .setNum(item.getCount());
            return stock;
        }).collect(Collectors.toList());

        // 2. 锁定库存
        for (SkuWareHasStockVo hasStockVo : hasStockVos) {
            // 当前商品是否成功锁住
            boolean skuLocked = false; // 所有商品是否都被锁住

            Long skuId = hasStockVo.getSkuId();
            if (null == hasStockVo.getWareIds() || hasStockVo.getWareIds().size() <= 0) {
                // 当前商品没有库存, 直接抛异常
                throw new NoStockException(skuId);
            }

            // 遍历所有仓库, 进行库存锁定
            for (Long wareId : hasStockVo.getWareIds()) {
                Long rows = wareSkuMapper.lockSkuStock(skuId, wareId, hasStockVo.getNum());
                if (rows > 0) {
                    // 锁库存成功
                    skuLocked = true;

                    // 增2. 商品锁定成功后, 保存工作单明细信息
                    WareOrderTaskDetail wareOrderTaskDetail = new WareOrderTaskDetail();
                    wareOrderTaskDetail.setSkuId(skuId)
                            .setSkuNum(hasStockVo.getNum())
                            .setTaskId(wareOrderTask.getId())
                            .setWareId(wareId)
                            .setLockStatus(1);
                    wareOrderTaskDetailService.save(wareOrderTaskDetail);

                    // 增3. 向 MQ 发送 库存锁定成功
                    rabbitTemplate.convertAndSend("stock-event-exchange", "stock.locked", new StockLockedTo(wareOrderTask.getId(), wareOrderTaskDetail.getId()));
                    break;
                }
            }

            // 如果当前商品没有锁住, 则直接抛异常
            if (!skuLocked) {
                throw new NoStockException(skuId);
            }
        }

        return true;
    }


    @Override
    public Boolean handleReleaseLockedStock(StockLockedTo stockLockedTo){
        // 1. 查询工作单详情, 若为空, 则数据已经混滚无需解锁 ; 若有数据则需要解锁
        WareOrderTaskDetail taskDetail = wareOrderTaskDetailService.getById(stockLockedTo.getTaskDetailId());
        if (null == taskDetail) {
            log.error("--> taskDetailId:{} 数据已回滚, 无需解锁", stockLockedTo.getTaskDetailId());
            return true;
        }

        // 2.解锁逻辑
        //  - 如果没有订单, 需要解锁库存
        //  - 如果有订单
        //      - 如果取消支付, 需解锁库存
        //      - 如果支付状态正常, 不需解锁库存

        // 如果 taskDetail 存在, 则 task 表也存在
        WareOrderTask orderTask = wareOrderTaskService.getById(stockLockedTo.getTaskId());
        // 根据订单号查询订单
        R<OrderVo> orderRes = orderFeignService.getOrderStatus(orderTask.getOrderSn());
        OrderVo order = orderRes.getData();
        if (!orderRes.isSuccess()) {
            return false;
        }

        if (null == order || order.getStatus().equals(OrderStatusEnum.CANCLED.getCode())){
            // 订单查询失败 或 订单未查询到 或 订单支付状态已取消 则 解锁
            this.unlockStock(taskDetail);
            log.info("--> 库存解锁成功 - taskDetailId:{}, wareId:{}, number:{}", stockLockedTo.getTaskDetailId(), taskDetail.getWareId(), taskDetail.getSkuNum());
        }
        return true;
    }


    @Override
    @Transactional
    public Boolean handleReleaseLockedStock(OrderVo orderVo){
        // 查询最新的库存工作单
        List<WareOrderTaskDetail> taskDetails = wareOrderTaskDetailMapper.getOrderTaskDetailByOrderSn(orderVo.getOrderSn());

        for (WareOrderTaskDetail taskDetail : taskDetails) {
            this.unlockStock(taskDetail);
        }

        return true;
    }



    private void unlockStock(WareOrderTaskDetail taskDetail){
        wareSkuMapper.unlockStock(taskDetail.getSkuId(), taskDetail.getWareId(), taskDetail.getSkuNum());

        taskDetail.setLockStatus(2);
        wareOrderTaskDetailService.updateById(taskDetail);
    }
}
